package com.thread.exe;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

public class LeftRightDeadLock {
	private final Object left = new Object();
	private final Object right = new Object();
	
	public void leftRight() {
		synchronized (left) {
			synchronized (right) {
				doSomething();
			}
		}
	}
	
	public void rightLeft() {
		synchronized (right) {
			synchronized (left) {
				doSomething();
			}
		}
	}

	private void doSomething() {}
	
	public void transferMoney(Account from, Account to, int amount) {
		synchronized (from) {
			synchronized (to) {
				from.debit(amount);
				to.crebit(amount);
			}
		}
	}
	
	private static final Object tieLock = new Object();
	public void transferMoneySafe(Account from, Account to, int amount) {
		int fromHash = System.identityHashCode(from);
		int toHash = System.identityHashCode(to);
		if(fromHash < toHash) {
			synchronized (from) {
				synchronized (to) {
					from.debit(amount);
					to.crebit(amount);
				}
			}
		}else if(fromHash > toHash) {
			synchronized (to) {
				synchronized (from) {
					from.debit(amount);
					to.crebit(amount);
				}
			}
		}else {
			synchronized (tieLock) {
				synchronized (to) {
					synchronized (from) {
						from.debit(amount);
						to.crebit(amount);
					}
				}
			}
		}
	}
	
	public boolean transferMoneyLock(Account from, Account to, int amount, long timeout, TimeUnit unit) throws InterruptedException {
		long fixedDelay = timeout / 5;
		long randMode = (long) (Math.random() * timeout);
		long stopTime = System.nanoTime() + unit.toNanos(timeout);
		while(true) {
			try {
				if(from.lock.tryLock()) {
					try {
						if(to.lock.tryLock()) {
							from.debit(amount);
							to.crebit(amount);
							return true;
						}
					} catch (Exception e) {
						e.printStackTrace();
					} finally {
						to.lock.unlock();
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				from.lock.unlock();
			}
			
			if(System.nanoTime() > stopTime)	// 书中这里是< 符号， 但与方法实际中的要求应该不符， 应该是没有超时 就尝试继续获取锁， 而不是 直接返回。
				return false;
			TimeUnit.NANOSECONDS.sleep(fixedDelay + randMode);
		}
	}
	
	class Account{
		public Lock lock;
		public void debit(int amount) {}
		public void crebit(int amount) {}
	}
}
